package grow

import (
	"server/game/cache"
	"server/game/itypes"
	"server/game/model"
	"server/share/config"
	"strings"

	"github.com/hwcer/cosgo/random"
	"github.com/hwcer/cosgo/registry"
	"github.com/hwcer/cosgo/slice"
	"github.com/hwcer/updater"
	"github.com/hwcer/updater/dataset"
	"github.com/hwcer/yyds"
	"github.com/hwcer/yyds/context"
	"github.com/hwcer/yyds/errors"
)

func init() {
	Register(&unit{})
}

type unit struct {
}

func (this *unit) Caller(node *registry.Node, handle *context.Context) interface{} {
	f := node.Method().(func(*unit, *context.Context) interface{})
	return f(this, handle)
}

/**
 * @name turnUnit
 * @param int id ID
 * 生成单位实例
 */
func (this *unit) TurnUnit(c *context.Context) any {
	id := c.GetInt32("id")
	if id == 0 {
		return errors.ErrArgEmpty
	}
	cfg := config.Data.UnitBase[id]
	if cfg == nil {
		return errors.ErrConfigEmpty
	}
	c.Player.Sub(id, 1)
	i, err := itypes.Unit.Create(c.Player.Updater, cfg.Id, 1)
	if err != nil {
		return err
	}
	coll := c.Player.Collection(config.ITypeUnit)
	if err = coll.New(i); err != nil {
		return err
	}

	return i.OID
}

/**
 * @name levelUp
 * @param string key 唯一ID
 * @param int expNum 消耗经验道具数量
 * 升级（强化）
 */
func (this *unit) LevelUp(c *context.Context) any {
	key := c.GetString("key")
	expNum := c.GetInt64("expNum")
	if key == "" || expNum <= 0 {
		return errors.ErrArgEmpty
	}
	items := cache.GetItems(c.Player.Updater)
	player := cache.GetRole(c.Player.Updater)
	role := player.All()

	data := items.Get(key)
	if data == nil {
		return errors.ErrDataNotExists
	} else if !yyds.Config.Is(data.IID, config.ITypeUnit) {
		return c.Error("不是角色")
	}
	unitConfig := config.Data.Unit[data.IID]
	if unitConfig == nil {
		return errors.ErrConfigEmpty
	}
	if role.SyncedUnit.Has(key) {
		return c.Error("角色被同步中，不可升级")
	}
	roleConfig := config.Data.PlayerExp[role.Lv]
	rlv := data.Attach.GetInt32(model.AttachUnitRlv)
	uk := unitConfig.UnitUpgradeId*1000 + rlv
	ug := config.Data.UnitUpgrade[uk]
	if ug == nil {
		return c.Errorf(0, "升阶配置不匹配")
	}

	maxLv := ug.MaxLevel
	lv := data.Attach.GetInt32(model.AttachUnitLv)

	if lv >= maxLv {
		return c.Error("你需要去升阶")
	}
	if maxLv > roleConfig.MaxLevel {
		maxLv = roleConfig.MaxLevel
	}
	if lv >= maxLv {
		return c.Error("已经是最大等级")
	}

	maxExpConfig := config.Data.UnitExp[maxLv]
	if maxExpConfig == nil {
		return c.Errorf(0, "最大等级的经验配置不存在:%d", maxLv)
	}
	//计算最大消耗经验值
	totalSub := int64(0)
	for i := lv; i <= maxLv; i++ {
		if ec := config.Data.UnitExp[i]; ec != nil && ec.Exp > 0 {
			totalSub += ec.Exp
		} else {
			break
		}
	}
	if expNum > totalSub {
		expNum = totalSub
	}

	totalLv := lv
	totalExp := data.Attach.GetInt64(model.AttachUnitExp) + expNum

	for totalLv <= maxLv {
		ec := config.Data.UnitExp[totalLv]
		if ec != nil && ec.Exp > 0 && totalExp >= ec.Exp {
			totalLv += 1
			totalExp -= ec.Exp
		} else {
			break
		}

	}

	var unitLevelUpItem int32
	base := config.Data.Base["unitLevelUpItem"]
	if base == nil {
		return c.Errorf(0, "base.unitLevelUpItem empty")
	}
	unitLevelUpItem = base.Val

	up := dataset.Update{}

	up[model.AttachUnitExp] = totalExp

	cost := map[int32]int64{}
	if err := data.Attach.Unmarshal(model.AttachUnitCostList, &cost); err != nil {
		return err
	}

	cost[unitLevelUpItem] += expNum
	up[model.AttachUnitCostList] = cost
	c.Player.Sub(unitLevelUpItem, expNum)

	if totalLv != lv {
		up[model.AttachUnitLv] = totalLv
	}

	if len(up) > 0 {
		items.SetAttachBatch(data.OID, up)
	}

	c.Player.Emit(config.Data.Events.HeroLevelUp, 1)
	return true
}

/**
* @name gradeUp
* @param string key 唯一ID
* @param int rlv 当前阶级  废弃
* 升阶段 突破
 */
func (this *unit) GradeUp(c *context.Context) any {
	key := c.GetString("key")
	if key == "" {
		return errors.ErrArgEmpty
	}
	items := cache.GetItems(c.Player.Updater)
	data := items.Get(key)
	if data == nil {
		return errors.ErrDataNotExists
	}
	uc := config.Data.Unit[data.IID]
	if uc == nil {
		return errors.ErrConfigEmpty
	}
	rlv := data.Attach.GetInt32(model.AttachUnitRlv)
	uk := uc.UnitUpgradeId*1000 + rlv
	ug := config.Data.UnitUpgrade[uk]
	if ug == nil {
		return c.Errorf(0, "升阶配置不匹配")
	}
	cost := map[int32]int64{}
	if err := data.Attach.Unmarshal(model.AttachUnitCostList, &cost); err != nil {
		return err
	}
	for _, sub := range ug.Sub {
		if sub.Id > 0 && sub.Num > 0 {
			c.Player.Sub(sub.Id, sub.Num)
			cost[sub.Id] += int64(sub.Num)
		}
	}
	next := rlv + 1
	up := dataset.Update{}
	up[model.AttachUnitCostList] = cost
	up[model.AttachUnitRlv] = next

	//装挂件
	if uc.PendantGroup > 0 && rlv == 0 {
		rdm := config.GetPendantGroupRandom(uc.PendantGroup)
		if rdm == nil {
			return c.Errorf(0, "PendantGroup Random Not Exist: %d", uc.PendantGroup)
		}
		k := rdm.Roll()
		pendantGroup := config.Data.PendantGroup[k]
		if pendantGroup == nil {
			return c.Errorf(0, "PendantGroup Not Exist: %d", k)
		}
		//随机出个位置
		pendantMax := config.Data.Base["pendantMax"]
		if pendantMax == nil {
			return c.Errorf(0, "base.PendantMax Not Exist")
		}
		i := random.Roll(0, pendantMax.Val-1)
		pendant := map[int32]int32{}
		if err := data.Attach.Unmarshal(model.AttachUnitPendent, &pendant); err != nil {
			return err
		}
		pendant[i] = pendantGroup.PendantId
		up[model.AttachUnitPendent] = pendant
	}
	items.SetAttachBatch(data.OID, up)
	c.Player.Emit(config.Data.Events.UnitUpgrade, next)
	return true
}

/*
*
  - @name starUp
  - @param string key 唯一ID
  - @param int slv 当前星级 废弃
  - @param string sub 消耗列表，用,切割
  - @param int num 消耗多少幼崽
  - 升星
*/
func (this *unit) StarUp(c *context.Context) any {
	key := c.GetString("key")
	sub := c.GetString("sub")
	num := c.GetInt32("num")
	if key == "" {
		return errors.ErrArgEmpty
	}
	var subArr []string
	if sub != "" {
		subArr = strings.Split(sub, ",")
		subArr = slice.Unrepeated(subArr)
	}

	items := cache.GetItems(c.Player.Updater)
	data := items.Get(key)
	if data == nil {
		return errors.ErrDataNotExists
	}
	unitConfig := config.Data.Unit[data.IID]
	if unitConfig == nil {
		return c.Errorf(0, "Unit Config Not Exist")
	}
	slv := data.Attach.GetInt32(model.AttachUnitSlv)
	refineKey := unitConfig.Rare*1000 + slv
	starUpConfig := config.Data.UnitStarup[refineKey]
	if starUpConfig == nil {
		return c.Errorf(0, "starUp Config Not Exist")
	}
	if starUpConfig.Num != num+int32(len(subArr)) {
		return c.Errorf(0, "消耗数量不对")
	}
	for _, k := range subArr {
		if k == key {
			return c.Errorf(0, "不能吃自己")
		}
		ss := items.Get(k)
		if ss == nil {
			return c.Errorf(0, "消耗的材料不存在")
		}
		if ss.Attach.GetInt32(model.AttachTypeLocked) > 0 {
			return c.Errorf(0, "锁定状态的单位无法被当做材料，请先解锁")
		}
		if ss.IID != data.IID {
			sConfig := config.Data.UnitStarUpMaterial[ss.IID]
			if sConfig == nil {
				return c.Errorf(0, "UnitStarUpMaterial Not Exist:%v", ss.IID)
			} else if !slice.Has(sConfig.UnitEvolutionId, data.IID) {
				return c.Errorf(0, "UnitStarUpMaterial Not Match:%v", ss.IID)
			}
		}

		items.Del(k)
	}
	if num > 0 {
		c.Player.Sub(unitConfig.BabyId, num)
	}
	items.SetAttach(data.OID, model.AttachUnitSlv, slv+1)
	return true
}

/**
* @name evolutionUp
* @param string key 唯一ID
* 单位进化
 */
func (this *unit) EvolutionUp(c *context.Context) any {
	key := c.GetString("key")
	if key == "" {
		return errors.ErrArgEmpty
	}
	items := cache.GetItems(c.Player.Updater)
	data := items.Get(key)
	if data == nil {
		return errors.ErrDataNotExists
	}
	evolutionConfig := config.Data.UnitEvolution[data.IID]
	if evolutionConfig == nil {
		return c.Errorf(0, "进化配置不存在或者无法进化")
	}
	if evolutionConfig.ItemNeed == 0 {
		return c.Errorf(0, "进化配置不匹配")
	}
	unitConfig := config.Data.Unit[data.IID]
	if unitConfig == nil {
		return errors.ErrConfigEmpty
	}
	c.Player.SubItems(evolutionConfig.Sub, 1)
	rd := random.New(nil)
	for _, g := range evolutionConfig.EvolutionGroup {
		if g.EvolutionId > 0 && g.Probability > 0 {
			rd.Add(g.EvolutionId, g.Probability)
		}
	}
	evolutionId := rd.Roll()
	if evolutionId <= 0 {
		return c.Errorf(0, "evolutionConfig Error:%d", data.IID)
	}

	newItem, _ := itypes.Unit.Create(c.Player.Updater, evolutionId, 1)
	var (
		oldSkill []int32
		newSkill []int32
	)
	if err := data.Attach.Unmarshal(model.AttachUnitSkill, &oldSkill); err != nil {
		return err
	}
	if err := newItem.Attach.Unmarshal(model.AttachUnitSkill, &newSkill); err != nil {
		return err
	}
	copy(newSkill, oldSkill)
	newItem.Attach = data.Attach

	if _, err := newItem.Attach.Marshal(model.AttachUnitSkill, newSkill); err != nil {
		return err
	}

	//copy(newItem.Unit.Skill, data.Unit.Skill)

	//up := dataset.Update{}
	//up["iid"] = evolutionId
	//切换技能
	//if len(data.SkillList) > 0 {
	//	skillList := make(map[int32]int32)
	//	oldSkillConfig := config.Data.SkillSets[data.IID]
	//	if oldSkillConfig == nil {
	//		return c.Errorf(0, "SkillSets Not Exist:%v", data.IID)
	//	}
	//	newSkillConfig := config.Data.SkillSets[evolutionId]
	//	if newSkillConfig == nil {
	//		return c.Errorf(0, "SkillSets Not Exist:%v", evolutionId)
	//	}
	//
	//	for i, k := range oldSkillConfig.Skills {
	//		if v, ok := data.SkillList[k]; ok && i < len(newSkillConfig.Skills) && newSkillConfig.Skills[i] > 0 {
	//			skillList[newSkillConfig.Skills[i]] = v
	//		}
	//	}
	//	newItem.SkillList = skillList
	//	//up["SkillList"] = skillList
	//}
	//返还升星材料
	slv := data.Attach.GetInt32(model.AttachUnitSlv)
	var returnNum int32
	for i := unitConfig.Rare; i < slv; i++ {
		k := unitConfig.Rare*1000 + i
		if refineConfig := config.Data.UnitStarup[k]; refineConfig != nil {
			returnNum += refineConfig.Num
		}
	}
	if returnNum > 0 {
		sConfig := config.Data.Decompose[unitConfig.Rare]
		if sConfig == nil {
			return c.Errorf(0, "Decompose Not Exist:%v", data.IID)
		}
		i := int(unitConfig.Element - 1)
		if i < 0 || i >= len(sConfig.DecomposeReceive) {
			return c.Errorf(0, "Decompose Error unitConfig:%d", unitConfig.Id)
		}
		c.Player.Add(sConfig.DecomposeReceive[i], returnNum)
	}
	//星级不继承
	newUnitConfig := config.Data.Unit[evolutionId]
	newItem.Attach.Set(model.AttachUnitSlv, newUnitConfig.Rare)
	//newItem.Slv = newUnitConfig.Race

	if err := items.New(newItem); err != nil {
		return err
	}
	items.Del(key)
	//装备处理
	c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
		equip := map[int32]string{}
		if err := data.Attach.Unmarshal(model.AttachUnitChipList, &equip); err == nil {
			for _, v := range equip {
				items.Equip.Use(v, newItem.OID)
			}
		}
		return false
	})

	//图鉴
	//collectColl := c.Player.Collection(config.ITypeCollect)
	//collectColl.Add(evolutionId, 1)
	return true
}

/**
* @name skillUp
* @param string key 唯一ID
* @param int skillID 技能
* 技能升级
 */

func (this *unit) SkillUp(c *context.Context) any {
	key := c.GetString("key")
	skillID := c.GetInt32("skillID")
	if key == "" || skillID <= 0 {
		return errors.ErrArgEmpty
	}

	items := cache.GetItems(c.Player.Updater)
	data := items.Get(key)
	if data == nil {
		return errors.ErrDataNotExists
	}
	unitConfig := config.Data.Unit[data.IID]
	if unitConfig == nil {
		return errors.ErrConfigEmpty
	}

	roleData := itypes.Role.All(c.Player.Updater)
	if roleData.SyncedUnit.Has(key) {
		return c.Errorf(0, "角色被同步中，不可以升级技能")
	}
	skillConfig := config.Data.SkillSets[data.IID]
	if skillConfig == nil {
		return c.Errorf(0, "SkillSets Not Exist:%v", data.IID)
	}
	index := slice.IndexOf(skillConfig.Skills, skillID)
	if index < 0 {
		return c.Errorf(0, "不存在的技能")
	}

	skill := make([]int32, len(skillConfig.Skills))
	if err := data.Attach.Unmarshal(model.AttachUnitSkill, &skill); err != nil {
		return err
	}
	lv := skill[index]
	if lv == 0 {
		lv = 1
	}

	lk := unitConfig.SkillUpgradeId*1000 + lv
	skillUpgradeConfig := config.Data.SkillUpgrade[lk]
	if skillUpgradeConfig == nil {
		return errors.ErrConfigEmpty
	}

	cost := map[int32]int64{}
	if err := data.Attach.Unmarshal(model.AttachUnitCostList, &cost); err != nil {
		return err
	}

	for _, v := range skillUpgradeConfig.Sub {
		if v.Id > 0 && v.Num > 0 {
			cost[v.Id] += int64(v.Num)
			c.Player.Sub(v.Id, v.Num)
		}
	}
	skill[index] = lv + 1

	up := dataset.Update{}
	up[model.AttachUnitSkill] = skill
	up[model.AttachUnitCostList] = cost
	items.SetAttachBatch(key, up)
	c.Player.Emit(config.Data.Events.HeroSkillUp, 1)
	return nil
}

/**
 * @name refine
 * @param string sub 角色ID串,以","分割
 * @param json unitBaby 幼体ID {iid:num}
 * 分解
 */

func (this *unit) Refine(c *context.Context) any {
	args := struct {
		Sub      string          `json:"sub"`
		UnitBaby map[int32]int32 `json:"unitBaby"`
	}{}
	if err := c.Bind(&args); err != nil {
		return err
	}
	arr := slice.Split(args.Sub, ",")
	arr = slice.Unrepeated(arr)

	var tot int32
	for _, v := range args.UnitBaby {
		if v <= 0 {
			return c.Errorf(0, "Unit Baby Empty")
		}
		tot += v
	}
	if tot <= 0 && len(arr) == 0 {
		return nil
	}
	role := itypes.Role.All(c.Player.Updater)
	items := cache.GetItems(c.Player.Updater)
	result := map[int32]int64{}
	var remove []string

	for _, uk := range arr {
		if role.SyncedUnit.Has(uk) {
			return c.Errorf(0, "角色在同步器内，不能进行分解:%s", uk)
		}
		ss := items.Get(uk)
		if ss == nil {
			return c.Errorf(0, "角色不存在:%s", uk)
		}
		if ss.Attach.GetInt32(model.AttachTypeLocked) > 0 {
			return c.Errorf(0, "锁定状态的单位无法分解，请先解锁")
		}

		unitConfig := config.Data.Unit[ss.IID]
		if unitConfig == nil {
			return c.Errorf(0, "Unit Config Not Exist:%v", ss.IID)
		}
		decomposeConfig := config.Data.Decompose[unitConfig.Rare]
		if decomposeConfig == nil {
			return c.Errorf(0, "Decompose Not Exist:%v", unitConfig.Rare)
		}
		if i := int(unitConfig.Element) - 1; i >= 0 && i < len(decomposeConfig.DecomposeReceive) && decomposeConfig.DecomposeReceive[i] > 0 {
			result[decomposeConfig.DecomposeReceive[i]] += 1
		}
		cost := map[int32]int64{}
		if err := ss.Attach.Unmarshal(model.AttachUnitCostList, &cost); err != nil {
			return err
		}
		for k, v := range cost {
			result[k] += v
		}
		remove = append(remove, uk)
		items.Del(uk)
	}
	if len(remove) > 0 {
		c.Player.Updater.On(updater.EventTypeSuccess, func(u *updater.Updater) (next bool) {
			items.Equip.Recycle(remove...)
			return false
		})
	}

	for k, v := range args.UnitBaby {
		babyConfig := config.Data.UnitBase[k]
		if babyConfig == nil {
			return c.Errorf(0, "角色数据不存在:%d", k)
		}
		unitConfig := config.Data.Unit[babyConfig.Id]
		if unitConfig == nil {
			return c.Errorf(0, "Unit Config Not Exist:%v", babyConfig.Id)
		}
		decomposeConfig := config.Data.Decompose[unitConfig.Rare]
		if decomposeConfig == nil {
			return c.Errorf(0, "Decompose Not Exist:%v", unitConfig.Rare)
		}
		if i := int(unitConfig.Element) - 1; i >= 0 && i < len(decomposeConfig.DecomposeReceive) && decomposeConfig.DecomposeReceive[i] > 0 {
			result[decomposeConfig.DecomposeReceive[i]] += int64(v)
		}
		c.Player.Sub(k, v)
	}
	for k, v := range result {
		c.Player.Add(k, v)
	}
	c.Player.Emit(config.Data.Events.UnitDecomposeNum, int32(len(remove))+tot)
	return nil
}
